<template>
  <el-row ref="tablePanel" class="sea-table">
    <!-- 表头配置，你可以设置一个很好看的表头，通常情况下，是放一个表单 -->
    <el-col>
      <slot name="header"></slot>
    </el-col>

    <el-col v-loading="loading" class="sea-table-body">

      <!-- Table列配置 -->
      <el-table ref="tableObject" border :data="list" :height="tableHeight"
                @sort-change="handlerSort" @row-click="onRowClick">

        <!-- 序号 -->
        <el-table-column v-if="enabledIndex" align="center" type="index" width="30" label="#"/>

        <!-- 复选框 -->
        <el-table-column v-if="enabledSelection" align="center" type="selection" width="46" style="text-align: center"/>

        <!-- 允许手动配置表头 -->
        <slot name="columns"></slot>
      </el-table>

      <!-- 抽屉配置，用于放置各类弹窗 -->
      <slot name="drawer"></slot>

      <!-- 分页配置 -->
      <div v-show="isPage" class="sea-table-footer">
        <el-pagination @size-change="handleLimitChange" @current-change="handlePageChange"
                       :total="total" :page-size="limit_" :current-page="page_"
                       :page-sizes="pageSizes"
                       layout="total, sizes, prev, pager, next, jumper">
        </el-pagination>
      </div>
    </el-col>
  </el-row>
</template>
<script>
/* eslint-disable */
import Layers from "@/lang/layers";
import ResultParser from "@/widget/provider/page-parser"

/**
 * 分页字段名配置
 */
const pageField = 'page';
const limitField = 'limit';

/**
 * 简单表格
 *
 * 定义了数据表格的整体布局，将表格和分页功能整合，并且对整体布局做了规划，
 * 如果期望保留所有功能，只是调整布局，修改这个组件即可。
 *
 * 插槽部分，预留了表头、自定义列、抽屉三个位置，供后续编程。
 *
 * <p>
 * 2023-02-28 开放单个可以触发联动查询的参数 where，where值的改变，直接触发查询
 */
export default {
    name: "SeaDataTable"
    , props: {
        // 数据 url
        url: {type: String, default: undefined},
        // 请求方式
        method: {type: String, default: 'GET'},
        // 值的改变会触发查询
        where: {type: Object, default: undefined},
        // 首次查询所需的参数，一般是表单的数值
        params: {type: Object, default: undefined},

        // 是否需要分页
        isPage: {type: Boolean, default: true},
        // 当前页码
        page: {type: Number, default: 1},
        // 每一页容量
        limit: {type: Number, default: 10},
        // 页号
        pageSizes: {type: Array, default: () => [10, 20, 50, 100]},

        // 表格高度，不要带单位
        tableHeight: {type: [Number, String], default: undefined},

        // 首次自动加载
        autoLoad: {type: Boolean, default: true},
        // 查询结果格式化
        resultParser: {type: Function, default: ResultParser},

        // 提交表单前置拦截，如果不满足条件，可以阻止表单提交
        beforeSubmit: {type: Function, default: undefined},
        // 表格数据后置处理，可能要进行格式转换啥的
        afterQuery: {type: Function, default: undefined},

        // 本地数据，直接指定数据列表
        data: {type: Array, default: undefined},
        // 允许表头序号，自动增加序号列
        enabledIndex: {type: Boolean, default: false},
        // 允许多选操作，自动增加复选框
        enabledSelection: {type: Boolean, default: false},
    }
    , data() {
        return {
            // 数据总条数
            total: undefined
            // 页面渲染实际使用的数据列表
            , list: this.data

            //页号
            , page_: this.page
            //行数
            , limit_: this.limit

            // 与 where 等效，会参与到表单序列化中
            , where_: undefined
            // 与 where 等效，会参与到表单序列化中
            , params_: undefined
            // 与 where 等效，会参与到表单序列化中
            , attach_: undefined

            // 数据载入状态条
            , loading: false
            // 表格高度
            , bodyHeight: undefined
        }
    }, provide() {
        return {
            // 直接通过 provide 提供当前实例，设计子组件的时候，可以很方便地调用 table 相关的功能
            dataTable: this
        }
    }, watch: {
        where: {
            handler: function (val) {
                this.handlerWhere(val);
            }
        }
        /**
         * 页号的变化，会触发页面查询
         */
        , page: {
            handler: function (newValue) {
                this.page_ = newValue;
                this.triggerChange()
            }
        }
        /**
         * 分页容量变化，会触发页面查询
         */
        , limit: {
            handler: function (newValue) {
                this.limit_ = newValue;
                this.triggerChange()
            }
        }
    }, created() {
        // 设置分页
        if (this.isPage) {
            this.attach_ = {page: this.page, limit: this.limit};
        } else {
            this.attach_ = {};
        }

        // 初始化查询条件
        this.where_ = this.where;

        // 表单参数
        this.params_ = this.params;
    }, mounted() {
        // 首次加载的时间尽量延迟，避免界面未完成初始化，就开始加载数据。
        // 没什么特别好的办法，界面是异步加载的，如果表格过早地渲染完成，会产生一些空指针异常。
        this.$nextTick(() => this.firstLoad());
    }, methods: {
        /**
         * 首次载入数据
         *
         * 首次加载是非常特殊的，
         * 需要获取分页、表单等各方的初始化数据，
         * 这些数据分布在各个插槽中，
         *
         * 插槽中的组件，生命周期是不可控的，在调用这个函数的时候，组件可能还没渲染完成，
         *
         * 因此这个函数很容易产生空指针异常，写法上，尽量保证所有数据载入完成。
         */
        firstLoad() {
            if (this.autoLoad && Layers.isNotEmpty(this.url)) {
                this.reload();
            }
        }
        /**
         * 触发查询，分页排序变化
         *
         * 需要有后台做对接
         *
         * @param column 排序变化事件
         */
        , handlerSort(column) {
            this.attach_.sort = encodeURI(column.prop + ' ' + column.order);
            this.reload();
        }
        /**
         * 触发查询，分页发生变化事件
         *
         * @param page 分页数据
         */
        , handlerPage: function (page) {
            this.attach_ = page;
            this.reload();
        }
        /**
         * 触发查询，表单数据发生变化
         *
         * @param params 表单数据
         */
        , handlerParams: function (params) {
            this.params_ = params;
            this.reload();
        }
        /**
         * 触发查询，表单数据发生变化
         *
         * @param where 附加参数
         */
        , handlerWhere: function (where) {
            this.where_ = where;
            this.reload();
        },

        /**
         * 组件内部页码发生变化
         *
         * @params page 页号配置
         */
        handlePageChange(page) {
            this.page_ = page;
            this.triggerChange();
        },
        /**
         * 组件内部分页数发生变化
         *
         * @params limit 页数
         */
        handleLimitChange(limit) {
            this.limit_ = limit;
            this.triggerChange();
        },
        /**
         * 分页变化事件
         */
        triggerChange: function () {
            let ret = {};
            ret[pageField] = this.page_;
            ret[limitField] = this.limit_;
            this.handlerPage(ret);
        }

        /**
         * 触发查询，分页发生变化事件
         *
         * @param page 分页数据
         */
        , setPage: function (page) {
            this.attach_ = page;
        }
        /**
         * 触发查询，表单数据发生变化
         *
         * @param params 表单数据
         */
        , setParams: function (params) {
            this.params_ = params;
        }
        /**
         * 触发查询，表单数据发生变化
         *
         * @param where 附加参数
         */
        , setWhere: function (where) {
            this.where_ = where;
        }
        /**
         * 优化设计：行点击事件，如果设置了复选框，点击后则变成选中
         *
         * @param row 行对象
         */
        , onRowClick: function (row) {
            if (this.enabledSelection) {
                this.$refs.tableObject.toggleRowSelection(row);
            }
            this.$emit('row-click', row);
        }
        /**
         * 获取选中的行，外部组件也是通过ref进行获取，设置 enabledSelection 为 true 的时候有效
         */
        , getSelection: function () {
            return this.$refs.tableObject.selection;
        }

        /**
         * 清除表格中的数据
         *
         * 用于解决弹窗打开关闭时候，数据闪烁的问题
         */
        , clearData: function () {
            this.list = undefined;
        }
        /**
         * 重新载入数据
         *
         * @param values 参数
         */
        , reload: function (values) {
            if (this.loading) {
                return;
            }
            // 构建请求参数
            let request = {};

            // 分页参数
            Layers.copy(this.attach_, request);
            // 表单参数
            Layers.copy(this.params_, request);
            // 额外指定的参数
            Layers.copy(this.where_, request);
            // 微调参数
            Layers.copy(values, request);

            // 前置拦截，进行逻辑判断，根据内容决定是否提交
            if (this.onSubmit !== undefined) {
                if (!this.onSubmit(request)) {
                    return;
                }
            }

            // ajax参数
            this.loading = true;
            AjaxProxy.ajax({
                url: this.url,
                method: this.method,
                params: request
            }).then((ret) => {
                let page = this.resultParser(ret);

                this.total = page.total;

                if (this.afterQuery === undefined) {
                    this.list = page.list;
                } else {
                    // 数据后置处理，如果有返回值，则使用返回值
                    let ret = this.afterQuery(page.list);
                    if (ret === undefined) {
                        this.list = page.list;
                    } else {
                        this.list = ret;
                    }
                }
            }).finally(() => {
                this.loading = false;
            })
        }
    }
    // end --
}
</script>
